namespace one {
	export class LoaderManager {

		private files: { key: string, priority: number }[];
		public constructor() {
			this.files = [];
		}

		private groupPros: { groups: { name: string, leftKeys: string[], total: number }[], onprogress: (current: number, total: number) => void }[] = [];
		/**
		 * 加载 group 内素材
		 * @param group 
		 * @param priority 
		 */
		public loadGroups(groups: string[], priority: number, onProgress: (current: number, total: number) => void = null): void {
			let totalKeys:string[] = [];

			let rs = [];
			for (let i = 0; i < groups.length; i++) {
				let list = RES.getGroupByName(groups[i]);
				let keys: string[] = [];
				
				for (let j: number = 0; j < list.length; j++) {
					if (!RES.getRes(list[j].name)) {
						keys.push(list[j].name);

						totalKeys.push(list[j].name);
					}
				}

				rs.push({
					name : groups[i],
					leftKeys: keys,
					total: list.length
				});
			}

			if (totalKeys.length == 0) {
				one.callLater(()=>{
					onProgress(100, 100);
				}, 1)
				return;
			}

			this.groupPros.push({
				groups: rs,
				onprogress: onProgress
			})
			
			this.load(totalKeys, priority);
		}

		private groups: { [group: string]: { keys: string[], onprogress: (current: number, total: number) => void, current: number, total: number } } = {};
		/**
		 * 加载 group 内素材
		 * @param group 
		 * @param priority 
		 */
		public loadGroup(group: string, priority: number, onProgress: (current: number, total: number) => void = null): void {
			let list = RES.getGroupByName(group);

			let keys: string[] = [];
			for (let i: number = 0; i < list.length; i++) {
				keys.push(list[i].name);
			}

			this.groups[group] = { keys: keys, onprogress: onProgress, current: 0, total: keys.length };
			this.load(keys, priority);
		}

		/**
		 * 对 group 内的素材停止加载
		 * @param group 
		 */
		public stopGroup(group: string): void {
			let list = RES.getGroupByName(group);

			let keys: string[] = [];
			for (let i: number = list.length; i >= 0; i--) {
				for (let j: number = this.files.length; j >= 0; j--) {
					if (this.files[j].key == list[i].name) {
						this.files.splice(j, 1);
					}
				}
			}
		}

		/**
		 * 暂停当前所有加载
		 */
		public pause(): void {
			this.isPause = true;
		}

		/**
		 * 恢复当前所有加载
		 */
		public goon(): void {
			this.isPause = false;

			this.loadNexts();
		}

		private listeners: { [key: string]: Function[] } = {};


		/**
		 * 将素材加入到加载队列
		 * @param keys 
		 * @param priority 
		 */
		public load(keys: string[], priority: number, call: () => void = null): void {
			for (let i: number = 0; i < this.files.length; i++) {
				let tempPri: number = this.files[i].priority;
				if (tempPri < priority) {
					this.insert(keys, priority, i, call);

					this.loadNexts();
					return;
				}
			}
			this.insert(keys, priority, this.files.length, call);
			this.loadNexts();
		}

		/**
		 * 对当前所有需要加载的权限降 1，最多降为 0
		 */
		public reducePriorities(): void {
			for (let i: number = 0; i < this.files.length; i++) {
				this.files[i].priority -= 1;

				this.files[i].priority = Math.max(this.files[i].priority, 0);
			}
		}

		private getExistIdx(key: string): number {
			for (let i: number = 0; i < this.files.length; i++) {
				if (this.files[i].key == key) {
					return i;
				}
			}
			return -1;
		}

		private addListener(key: string, call: () => void): void {
			if (this.listeners[key] == null) {
				this.listeners[key] = [call];
			}
			else {
				this.listeners[key].push(call);
			}
		}

		private insert(keys: string[], priority: number, index: number, call: () => void): void {
			for (let j: number = 0; j < keys.length; j++) {
				let idx: number = this.getExistIdx(keys[j]);
				if (idx == -1) {
					this.files.splice(index + j, 0, { key: keys[j], priority: priority });
					if (call != null) {
						this.addListener(keys[j], call);
					}
				}
				else {
					if (idx <= index + j) {//原来的权限比此权限大
						if (call != null) {
							this.addListener(keys[j], call);
						}
						index--;
					}
					else {
						this.files.splice(idx, 1);

						this.files.splice(index + j, 0, { key: keys[j], priority: priority });
						if (call != null) {
							this.addListener(keys[j], call);
						}
					}
				}
			}
		}

		private loadNexts(): void {
			one.setTimeout(() => {
				for (let i: number = this.loadingCount; i < this.maxLoadingThread; i++) {
					this.loadFile();
				}
			}, 0);
		}

		private isPause: boolean = false;

		/**
		 * 设置当前同时加载的数量，默认为 2
		 */
		maxLoadingThread: number = 2;
		private loadingCount: number = 0;
		private loadFile(): void {
			if (this.files.length <= 0) {
				return;
			}

			if (this.loadingCount >= this.maxLoadingThread) {
				return;
			}

			if (this.isPause) {
				return;
			}

			this.loadingCount++;

			let file = this.files.shift();

			let loadOver = () => {
				for (let i: number = 0; this.listeners[file.key] && i < this.listeners[file.key].length; i++) {
					this.listeners[file.key][i]();
				}
				delete this.listeners[file.key];

				for (let key in this.groups) {
					let idx = this.groups[key].keys.indexOf(file.key);
					if (idx >= 0) {
						this.groups[key].current++;
						this.groups[key].keys.splice(idx, 1);

						if (this.groups[key].onprogress) {
							this.groups[key].onprogress(this.groups[key].current, this.groups[key].total);
						}

						if (this.groups[key].keys.length == 0) {
							delete this.groups[key];
						}
					}
				}

				for (let i = 0; i < this.groupPros.length; i++) {
					let lefts = 0;
					let totals = 0;
					for (let j = 0; j < this.groupPros[i].groups.length; j++) {
						let leftKeys = this.groupPros[i].groups[j].leftKeys;
						for (let k = 0; k < leftKeys.length; k++) {
							let iddx = leftKeys.indexOf(file.key);
							if (iddx >= 0) {
								leftKeys.splice(iddx, 1);
							}
						}
						lefts += leftKeys.length;
						totals += this.groupPros[i].groups[j].total;
					}

					if (this.groupPros[i].onprogress) {
						this.groupPros[i].onprogress(totals - lefts, totals);

						if (lefts == 0) {
							this.groupPros.splice(i, 1);
							i--;
						}
					}
				}

				this.loadingCount--;
				this.loadFile();
			};

			if (RES.getRes(file.key)) {
				loadOver();
			}
			else {
				RES.getResAsync(file.key, () => {
					loadOver();
				}, this);
			}

		}
	}

	export let loaderManager = new LoaderManager();
}
